package com.example.javapoet.demo.advanced;

import com.squareup.javapoet.*;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.tools.Diagnostic;
import java.io.IOException;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.List;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * JavaPoet高级示例 - 生成注解处理器代码
 */
public class AnnotationProcessorGeneration {

    public static void main(String[] args) {
        // 生成注解处理器类
        generateAnnotationProcessor();
        
        // 生成自定义注解
        generateCustomAnnotation();
    }

    /**
     * 生成注解处理器类
     */
    public static void generateAnnotationProcessor() {
        // 创建注解处理器类
        TypeSpec.Builder processorBuilder = TypeSpec.classBuilder("MyAnnotationProcessor")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(SupportedAnnotationTypes.class)
                        .addMember("value", "$S", "com.example.annotations.*")
                        .build())
                .addAnnotation(AnnotationSpec.builder(SupportedSourceVersion.class)
                        .addMember("value", "$T.RELEASE_21", SourceVersion.class)
                        .build())
                .addSuperinterface(ClassName.get(Processor.class));
        
        // 添加init方法
        MethodSpec initMethod = MethodSpec.methodBuilder("init")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(ProcessingEnvironment.class, "processingEnv")
                .addStatement("super.init(processingEnv)")
                .addStatement("this.processingEnv = processingEnv")
                .build();
        
        processorBuilder.addField(FieldSpec.builder(ProcessingEnvironment.class, "processingEnv")
                .addModifiers(Modifier.PRIVATE)
                .build());
        
        // 添加process方法
        MethodSpec processMethod = MethodSpec.methodBuilder("process")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(boolean.class)
                .addParameter(ParameterizedTypeName.get(
                        ClassName.get(Set.class),
                        ClassName.get(TypeElement.class)), "annotations")
                .addParameter(RoundEnvironment.class, "roundEnv")
                .beginControlFlow("for ($T element : roundEnv.getElementsAnnotatedWith($T.class))", 
                        Element.class, ClassName.get("com.example.annotations", "MyAnnotation"))
                .addStatement("$T message = $S + element.getSimpleName()", String.class, "Processing element: ")
                .addStatement("processingEnv.getMessager().printMessage($T.Kind.NOTE, message, element)", 
                        Diagnostic.class)
                .endControlFlow()
                .addStatement("return true")
                .build();
        
        // 添加getSupportedAnnotationTypes方法
        MethodSpec getSupportedAnnotationTypesMethod = MethodSpec.methodBuilder("getSupportedAnnotationTypes")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(ParameterizedTypeName.get(
                        ClassName.get(Set.class),
                        ClassName.get(String.class)))
                .addStatement("$T<$T> types = new $T<>()", 
                        Set.class, String.class, ClassName.get("java.util", "HashSet"))
                .addStatement("types.add($S)", "com.example.annotations.MyAnnotation")
                .addStatement("return types")
                .build();
        
        // 添加getSupportedSourceVersion方法
        MethodSpec getSupportedSourceVersionMethod = MethodSpec.methodBuilder("getSupportedSourceVersion")
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .returns(SourceVersion.class)
                .addStatement("return $T.RELEASE_21", SourceVersion.class)
                .build();
        
        // 构建类
        TypeSpec processorClass = processorBuilder
                .addMethod(initMethod)
                .addMethod(processMethod)
                .addMethod(getSupportedAnnotationTypesMethod)
                .addMethod(getSupportedSourceVersionMethod)
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.processor", processorClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("生成MyAnnotationProcessor类:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成MyAnnotationProcessor.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成自定义注解
     */
    public static void generateCustomAnnotation() {
        // 创建注解类
        TypeSpec annotationClass = TypeSpec.annotationBuilder("MyAnnotation")
                .addModifiers(Modifier.PUBLIC)
                .addAnnotation(AnnotationSpec.builder(Retention.class)
                        .addMember("value", "$T.RUNTIME", RetentionPolicy.class)
                        .build())
                .addAnnotation(AnnotationSpec.builder(Target.class)
                        .addMember("value", "$T.TYPE", TypeElement.class)
                        .build())
                .addMethod(MethodSpec.methodBuilder("value")
                        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                        .defaultValue("\"default\"")
                        .returns(String.class)
                        .build())
                .addMethod(MethodSpec.methodBuilder("priority")
                        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                        .defaultValue("1")
                        .returns(int.class)
                        .build())
                .build();
        
        // 创建Java文件
        JavaFile javaFile = JavaFile.builder("com.example.annotations", annotationClass)
                .build();

        try {
            // 输出到控制台
            System.out.println("\n生成MyAnnotation注解:");
            javaFile.writeTo(System.out);
            
            // 输出到文件
            Path path = Paths.get("src/main/java");
            javaFile.writeTo(new File(path.toUri()));
            System.out.println("成功生成MyAnnotation.java文件");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}